/*
 Xern Entity Relocation Network
 Copyright (C) 2010-2011 Ronie Salgado <roniesalg@gmail.com>

 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
 License as published by the Free Software Foundation; either
 version 2.1 of the License, or (at your option) any later version.

 This library is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 Lesser General Public License for more details.

 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
*/

#include <OgreLight.h>
#include <boost/lexical_cast.hpp>
#include "Xern/FilterStream.hpp"
#include "Xern/Log.hpp"
#include "Light.hpp"
#include "OgreRenderer.hpp"

namespace XernOgre
{
	Light::Light(Ogre::SceneManager *scene, Ogre::Light *light)
		: scene(scene), light(light)
	{
		light->setCastShadows(true);
		id = nil_uuid();
	}

	Light::~Light()
	{
		scene->destroyLight(light);
	}

	Ogre::MovableObject *Light::GetMovable()
	{
		return light;
	}

	void Light::DelayAttach(boost::shared_ptr<SceneNode> node)
	{
		printf("delay attach light\n");
	}

	IResource::ResourceFlags Light::GetFlags() const
	{
		return ResourceFlags(RFL_MOVABLE_OBJECT | RFL_SERIALIZABLE);
	}

	void Light::UpdateState(boost::shared_ptr<IDataFilter> stateData)
	{
	}

	const uuid &Light::GetId() const
	{
		return this->id;
	}

	void Light::SetId(const uuid &id)
	{
		this->id = id;
	}

	const char *Light::GetMimeType() const
	{
		return "application/vnd.xern.light";
	}

	const char *Light::GetFileExtension() const
	{
		return "";
	}

	bool Light::IsFileSerializable() const
	{
		return false;
	}

	bool Light::IsMemorySerializable() const
	{
		return true;
	}

	boost::shared_ptr<IDataFilter> Light::GetSerializedData() const
	{
		// Write the light data into an object
		JsonObject lightData;

		// Write the type.
		switch(light->getType())
		{
		case Ogre::Light::LT_DIRECTIONAL:
			lightData["type"] = "directional";
			break;
		case Ogre::Light::LT_SPOTLIGHT:
			lightData["type"] = "spot";
			break;
		case Ogre::Light::LT_POINT:
		default:
			lightData["type"] = "point";
			break;
		}

		// Write the attenuation.
		JsonObject attenuation;
		attenuation["range"] = light->getAttenuationRange();
		attenuation["constant"] = light->getAttenuationConstant();
		attenuation["linear"] = light->getAttenuationLinear();
		attenuation["quadratic"] = light->getAttenuationQuadric();
		lightData["attenuation"] = attenuation;

		// Write the direction.
		lightData["direction"] = boost::lexical_cast<std::string> (GetDirection());

		// Write the colors.
		lightData["diffuse"] = boost::lexical_cast<std::string> (GetDiffuseColor());
		lightData["specular"] = boost::lexical_cast<std::string> (GetSpecularColor());

		// Write shadow casting.
		lightData["cast_shadows"] = IsShadowCasting();

		// Serialize the object.
		boost::shared_ptr<StringFilter> filter(new StringFilter());
		filter->GetStream() << lightData;
		return filter;

	}

	ILight::LightType Light::GetType() const
	{
		switch(light->getType())
		{
		case Ogre::Light::LT_POINT:
			return LT_POINT;
		case Ogre::Light::LT_DIRECTIONAL:
			return LT_DIRECTIONAL;
		case Ogre::Light::LT_SPOTLIGHT:
			return LT_SPOT;
		default:
			assert(NULL);
			return LT_POINT;
		}
	}

	void Light::SetType(LightType type)
	{
		switch(type)
		{
		case LT_POINT:
			light->setType(Ogre::Light::LT_POINT);
			break;
		case LT_DIRECTIONAL:
			light->setType(Ogre::Light::LT_DIRECTIONAL);
			break;
		case LT_SPOT:
			light->setType(Ogre::Light::LT_SPOTLIGHT);
			break;
		default:
			assert(NULL);
		}
	}

	Color Light::GetDiffuseColor() const
	{
		const Ogre::ColourValue &c = light->getDiffuseColour();
		return Color(c.r, c.g, c.b);
	}

	void Light::SetDiffuseColor(const Color &c)
	{
		light->setDiffuseColour(c.r, c.g, c.b);
	}

	Color Light::GetSpecularColor() const
	{
		const Ogre::ColourValue &c = light->getSpecularColour();
		return Color(c.r, c.g, c.b);
	}

	void Light::SetSpecularColor(const Color &c)
	{
		light->setSpecularColour(c.r, c.g, c.b);
	}

	Vector3 Light::GetDirection() const
	{
		const Ogre::Vector3 &d = light->getDirection();
		return Vector3(d.x, d.y, d.z);
	}

	void Light::SetDirection(const Vector3 &direction) const
	{
		light->setDirection(direction.x, direction.y, direction.z);
	}

	void Light::GetAttenuation(float &range, float &constant, float &linear, float &quadratic) const
	{
		range = light->getAttenuationRange();
		constant = light->getAttenuationConstant();
		linear = light->getAttenuationLinear();
		quadratic = light->getAttenuationQuadric();
	}

	void Light::SetAttenuation(float range, float constant, float linear, float quadratic)
	{
		light->setAttenuation(range, constant, linear, quadratic);
	}

	void Light::GetSpotRange(float &innerAngle, float &outerAngle, float &falloff)
	{
		innerAngle = light->getSpotlightInnerAngle().valueDegrees();
		outerAngle = light->getSpotlightInnerAngle().valueDegrees();
		falloff = light->getSpotlightFalloff();
	}

	void Light::SetSpotRange(float innerAngle, float outerAngle, float falloff)
	{
		light->setSpotlightRange(Ogre::Degree(innerAngle), Ogre::Degree(outerAngle), falloff);
	}

	void Light::SetTransform(const ReversibleTransform &trans)
	{
		transform = trans;

		const Vector3 &pos = trans.GetVector();
		light->setPosition(pos.x, pos.y, pos.z);
	}

	const ReversibleTransform &Light::GetTransform() const
	{
		return transform;
	}

	bool Light::IsShadowCasting() const
	{
		return light->getCastShadows();
	}

	void Light::SetShadowCasting(bool value)
	{
		light->setCastShadows(value);
	}

	boost::shared_ptr<Light>
	Light::LoadLight(boost::shared_ptr<OgreRenderer> renderer, const JsonObject &data)
	{
		try
		{
			// Create the light.
			Ogre::SceneManager *scene = renderer->GetSceneManager();
			boost::shared_ptr<Light> light(new Light(scene, scene->createLight()));

			// Read the light type.
			const JsonString &type = boost::get<JsonString> (data["type"]);
			if(type == "spot")
				light->SetType(LT_SPOT);
			else if(type == "directional")
				light->SetType(LT_DIRECTIONAL);
			else // if(type == "point")
				light->SetType(LT_POINT);

			light->SetShadowCasting(boost::get<JsonNumber> (data["cast_shadows"]));
			light->SetDirection(boost::lexical_cast<Vector3> (boost::get<JsonString> (data["direction"]).string()));
			light->SetDiffuseColor(boost::lexical_cast<Color> (boost::get<JsonString> (data["diffuse"]).string()));
			light->SetSpecularColor(boost::lexical_cast<Color> (boost::get<JsonString> (data["specular"]).string()));

			if(data.has_key("attenuation"))
			{
				const JsonObject &attenuation = boost::get<JsonObject> (data["attenuation"]);
				light->SetAttenuation(
						boost::get<JsonNumber> (attenuation["range"]),
						boost::get<JsonNumber> (attenuation["constant"]),
						boost::get<JsonNumber> (attenuation["linear"]),
						boost::get<JsonNumber> (attenuation["quadratic"]));
			}

			// Return the light.
			return light;
		}
		catch(const XernException &e)
		{
			LogConsole->Error("Failed to read light data: %s", e.what());
		}
		catch(const boost::bad_get &e)
		{
			LogConsole->Error("Incomplete light data.");
		}

		return boost::shared_ptr<Light> ();
	}
}; // namespace Xern
